/*=============================================================================
*
*   Copyright (C) 2020 All rights reserved.
*
*   Filename:		hierarchy.hpp
*
*   Author: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Date:  2020-01-17 16:52
*
*   Last Editors: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Last modified:	2020-03-23 01:54
*
*   Description: This Hierarchy system is borrowed from DOLFIN at
https://github.com/FEniCS/dolfin.git under the terms of the GNU Lesser General
Public License as published by the Free Software Foundation, either version 3 of
the License, or any later version.
*
=============================================================================*/
#if !defined(TEMPLATE_CLASS_FOR_HIERARCHICAL_STRUCTURE)
#define TEMPLATE_CLASS_FOR_HIERARCHICAL_STRUCTURE

#include <iostream>
#include <memory>
#include <vector>

namespace geoxfem::util {

template <typename T>
struct Hierarchy {

  /*constructors and destructor{{{*/

  /**
   * @brief Hierarchy
   *
   * @param _pSelf
   */
  Hierarchy() : m_list_child{}, m_parent{nullptr} {}
  Hierarchy(T* pParent) : m_list_child{}, m_parent{pParent} {}
  Hierarchy(const Hierarchy<T>& rhs) = default;
  Hierarchy(Hierarchy<T>&& rhs)      = default;
  ~Hierarchy() {
    for(auto v : m_list_child)
      delete v;
  }
  Hierarchy<T>& operator=(const Hierarchy& rhs) = default;
  Hierarchy<T>& operator=(Hierarchy&& rhs) = default;
  /*}}}*/
  /*member manuvar{{{*/

  /**
   * @brief Set parent
   *
   * @param _pParent
   */
  void setParent(T* _pParent) {
    m_parent = _pParent;
  }
  /**
   * @brief Set child
   *
   * @param _pChild
   */
  void addChild(T* child) {
    child->setParent(static_cast<T*>(this));
    m_list_child.emplace_back(child);
  }
  template <typename... argsList>
  void addNewChild(argsList... args) {
    addChild(new T(args...));
  }
  void deleteChild(T* child) {
    auto it  = std::find_if(m_list_child.begin(), m_list_child.end(),
                           [child](T* _child) { return *child == *_child; });
    T*   tmp = *it;
    m_list_child.erase(it);
    delete tmp;
    return;
  }
  /**
   * @brief clearChild
   */
  void clearChild() {
    m_list_child.clear();
  }
  /*}}}*/

  /**
   * @brief Return my depth of the hierarchy; that is, the total number of depth
   * from root parent to the current object via child-parent relationships,
   * including the object itself.
   *
   * @return
   */
  size_t depth() const {
    size_t d  = 1;
    auto   it = this;
    for(; it->m_parent; it = it->m_parent)
      d++;
    return d;
  }
  /**
   * @brief Check if the object has a parent.
   *
   * @return
   */
  bool hasParent() const {
    return m_parent != nullptr;
  }
  /**
   * @brief Check if the object has a child.
   *
   * @return
   */
  bool hasChild() const {
    return !m_list_child.empty();
  }
  /**
   * @brief get number of child objects
   *
   * @return
   */
  size_t getNumberOfChild() const {
    return m_list_child.size();
  }
  /**
   * @brief get #n child in child list
   *
   * @param n
   * @return
   */
  const T* getChild(const size_t& n) const {
    return m_list_child.at(n);
  }
  T* getChild(const size_t& n) {
    return m_list_child.at(n);
  }

  std::vector<T*>& getChildren() {
    return m_list_child;
  }
  /**
   * @brief iterator for begin of child list. for entityIterator use
   *
   * @return
   */
  /* typename std::vector<T>::const_iterator getChildBegin() const { */
  /*   return m_list_child.begin(); */
  /* } */
  /**
   * @brief iterator for end of child list. for entityIterator use
   *
   * @return
   */
  /* typename std::vector<T>::const_iterator getChildEnd() const { */
  /*   return m_list_child.end(); */
  /* } */
  /**
   * @brief Return parent in hierarchy
   *
   * @return
   */
  T getParent() const {
    // if (!m_parent)
    //   geoxfem_error("Hierarchy.h", "extract parent of hierarchical object",
    //                 "Object has no parent in hierarchy");
    return *m_parent;
  }
  /**
   * @brief Return the root parent node
   *
   * @return
   */
  T& getAlphaOne() {
    T* it = static_cast<T*>(this);
    for(; it->m_parent; it = it->m_parent)
      ;
    return *it;
  }

private:
  /// vector of smart pointer to store child nodes
  std::vector<T*> m_list_child;

  /// pointer to parent node
  T* m_parent;

  friend T;
};

}  // namespace geoxfem::util

#endif  // _TEMPLATE_CLASS_FOR_HIERARCHICAL_STRUCTURE
